/**
 * @author {Yuntao Zhou}
 */
if (!Util) {
    var Util = {};
}
Util.Calendar=function(domEL,callback,config){
    this.dom=domEL;
    this.config={
        autoHide:true,
        autoLocation:true,
        direction:'bottom',
        showFootbar:true
    };
    if(config){
        $.extend(this.config,config);
    }
    this.callback={};
    if (typeof callback == 'function') {
        this.callback['default'] = callback;
    }
    this.dayNames=['Su','Mo','Tu','We','Th','Fr','Sa'];
    this.monthNames=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
    this.el=null;
    this.tbarEl=null;
    this.bodyEl=null;
    this.fbarEl=null;
    this.currentDOMCalID=null;
    this.curSelectDay=null;
    this.lastSelectDay=null;
    this._isSelect=true;
    this.domID=1;
    this.init();
};
Util.Calendar.prototype={
    init:function(){
        this.el=$('<div class="qs-calendar">'+
                  '<div class="qs-calendar-tbar">'+
                    '<div class="tool">'+
                        '<div class="navBtn prevYear"><div class="py"></div></div>'+
                        '<div class="navBtn nextYear"><div class="ny"></div></div>'+
                        '<div class="navBtn prevMonth"><div class="pm"></div></div>'+
                        '<div class="navBtn nextMonth"><div class="nm"></div></div>'+
                        '<div class="titleCont"></div>'+
                    '</div>'+
                    '<div class="dayNames"></div>'+
                  '</div>'+
                  '<div class="qs-calendar-body"><table cellspacing="0" cellpadding="0" align="center"></table></div>'+
                  '<div class="qs-calendar-fbar"><div class="btnToday">Today</div></div></div>'
        ).appendTo(this.dom);
        this.tbarEl=this.el.find('.qs-calendar-tbar');
        this.bodyEl=this.el.find('.qs-calendar-body');
        this.fbarEl=this.el.find('.qs-calendar-fbar');
        if (!this.config.showFootbar) {
            this.fbarEl.hide();
        }
        this._createWeekPane()
        this._createDayPane();
        this._bindEvent();
    },
    _createWeekPane:function(){
        var html='';
        for(var i=0,l=this.dayNames.length;i<l;i++){
            if (this.dayNames[i] == 'Su' || this.dayNames[i] == 'Sa') {
                html += '<div class="weekend weekNum">' + this.dayNames[i] + '</div>';
            }else{
                html += '<div class="weekNum">' + this.dayNames[i] + '</div>';
            }
        }
        this.tbarEl.find('.dayNames')[0].innerHTML=html;
    },
    _createDayPane:function(date){
        if(!date||date==null){
            date=new Date();
        }else{      
            if (typeof date == 'string') {
                date = this._convertDate(date);
            }   
        }
        var dayArray=[];
        var firstDate=this._getFirstDay(date);
        var lastDate=this._getLastDay(date);
        var firstWD=firstDate['wd'];
        var lastWD=lastDate['wd'];
        var d;
        if(firstWD!=0){//first day equals sunday    
            for(var i=firstWD;i>=1;i--){
                d=new Date(firstDate['date'].getTime()-24*1000*60*60*i);
                dayArray.push({
                    day:d.getDate(),
                    date:d,
                    isCurMonth:false,
                    isWeekend:this._isWeekend(d),
                    isSelectDay:this._isSelectDay(d),
                    isToday:this._isToday(d)
                });
            }
        }
        for(var i=firstDate['day'];i<=lastDate['day'];i++){
            d=new Date(firstDate['year'],firstDate['month'],i);
            dayArray.push({
                day:d.getDate(),
                date:d,
                isCurMonth:true,
                isWeekend:this._isWeekend(d),
                isSelectDay:this._isSelectDay(d),
                isToday:this._isToday(d)
            });
        }
        if(lastWD!=6){//last day equals sat
            for(var i=1;i<=6-lastWD;i++){
                d=new Date(lastDate['date'].getTime()+24*1000*60*60*i);
                dayArray.push({
                    day:d.getDate(),
                    date:d,
                    isCurMonth:false,
                    isWeekend:this._isWeekend(d),
                    isSelectDay:this._isSelectDay(d),
                    isToday:this._isToday(d)
                });
            }
        }
        this._setTitleCont(date);
        this.bodyEl.find('table').html(this._createTableHtml(dayArray));//under IE，table innerHTML is read only. 
    },
    _setTitleCont:function(date){
        var titleContEl=this.tbarEl.find('.titleCont');
        if(titleContEl.attr('value')!=this._formatDate(date)){
            var y=date.getFullYear(),m=date.getMonth();
            titleContEl.attr('value',this._formatDate(date)).html(this.monthNames[m]+" "+y);
        }   
    },
    _createTableHtml:function(dayArray){
        var len=dayArray.length;
        var html='',rowArray=[];
        for(var i=0;i<len;i++){ 
            rowArray.push(dayArray[i]);
            if(rowArray.length==7){
                html+=this._createRowHtml(rowArray);
                rowArray=[];
            }
        }
        return html;
    },
    _createRowHtml:function(rowArray){
        var html='<tr>',cls;
        for(var i=0,l=rowArray.length;i<l;i++){
            if(rowArray[i].isCurMonth&&!rowArray[i].isWeekend){
                cls='dayNum';
            }else if(!rowArray[i].isCurMonth&&!rowArray[i].isWeekend){
                cls='dayNum otherMonth';
            }else if(!rowArray[i].isCurMonth&&rowArray[i].isWeekend){
                cls='dayNum otherMonthweekend';
            }else if(rowArray[i].isCurMonth&&rowArray[i].isWeekend){
                cls='dayNum weekend';
            }else{
                cls='dayNum';
            }
            if(rowArray[i].isToday){
                cls='dayNum today';
            }
            if(rowArray[i].isSelectDay){
                cls='dayNum selectday';
            }
            html+='<td><div class="'+cls+'" val="'+this._formatDate(rowArray[i]['date'])+'">'+rowArray[i]['day']+'</div></td>';
        }
        html+='</tr>';
        return html;
    },
    _getFirstDay:function(date){
        var year=date.getFullYear(),
            month=date.getMonth();
        var fd=new Date(year,month,1);
        return {
            date:fd,
            year:year,
            month:month,
            day:1,
            wd:fd.getDay()
        }
    },
    _getLastDay:function(date){
        var year=date.getFullYear(),
            month=date.getMonth();
        var d=new Date(year,month+1,1);
        var ld=new Date(d.getTime()-24*1000*60*60);
        return {
            date:ld,
            year:year,
            month:month,
            day:ld.getDate(),
            wd:ld.getDay()
        }
    },
    _formatDate:function(date){
        return date.getFullYear()+"-"+this._formateNum(date.getMonth()+1)+"-"+this._formateNum(date.getDate());
    },
    _convertDate:function(str){//yyyy-mm-dd
        if (!str||str == null || str == '') {
            return null;
        }else {
            var y = str.substring(0, 4), m = str.substring(5, 7), d = str.substring(8, 10);
            return new Date(parseInt(y, 10), parseInt(m, 10) - 1, parseInt(d, 10));
        }
    },
    _formateNum:function(d){
        return d<10?("0"+d):d;
    },
    _isDate:function(str){//check the date format yyyy-mm-dd or yyyy/mm/dd or yyyy.mm.dd
        var reg = /^(\d{4})(-|\/|\.)(\d{1,2})\2(\d{1,2})$/;
        var result = str.match(reg);
         if (result == null) return false; 
         var y, m, d;
         y = result[1]; //year number
         m = parseInt(result[3], 10); //month number
         d = parseInt(result[4], 10); //day 
         if ((m < 1) || (m > 12) || (d < 1) || (d > 31)) return false;
         if (((m == 4) || (m == 6) || (m == 9) || (m == 11)) && (d > 30)) return false;
         if ((y % 4) == 0) {
            if ((m == 2) && (d > 29)) return false;
         } else {
            if ((m == 2) && (d > 28)) return false;
         }
         return true;
    },
    _isWeekend:function(date){
        return (date.getDay()==0||date.getDay()==6)?true:false;
    },
    _isSelectDay:function(date){
        if (this.curSelectDay != null&&this.lastSelectDay !=null){
            return this.lastSelectDay.getMonth()==date.getMonth()&&date.getFullYear() == this.curSelectDay.getFullYear() && date.getMonth() == this.curSelectDay.getMonth() && date.getDate() == this.curSelectDay.getDate();
        }
        return false;
    },
    _isToday:function(date){
        var curDate=new Date();
        return date.getFullYear()==curDate.getFullYear()&&date.getMonth()==curDate.getMonth()&&date.getDate()==curDate.getDate();
    },
    _bindEvent:function(){
        var self=this;
        this.el.bind('click.cal',function(e){
            var target=$(e.target);
            if(target.hasClass('py')){
                self._decrease('Y');
            }else if(target.hasClass('ny')){
                self._increase('Y');
            }else if(target.hasClass('pm')){
                self._decrease('M');
            }else if(target.hasClass('nm')){
                self._increase('M');
            }else if(target.hasClass('dayNum')){
                self._onClick(self._convertDate(target.attr('val')));
            }else if(target.hasClass('btnToday')){
                self._onClick(self._convertDate(self._formatDate(new Date())));
            }
        });
        $(document).bind("mousedown.cal",function(e){
            var target=$(e.target);
            if(!$.contains(self.el[0],e.target)&&!self.callback[target.attr('calID')]){
                self.hide();
            }
        });
    },
    _increase:function(type){
        var date=this._convertDate(this.tbarEl.find('.titleCont').attr('value'));
        var y=date.getFullYear();
        var m=date.getMonth();
        var d=date.getDate();
        var d;
        if(type=='Y'){
            d=new Date(y+1,m,d);
        }else if(type=='M'){
            if(m==11){
                d=new Date(y+1,0,d);
            }else{
                d=new Date(y,m+1,d);
            }               
        }
        this.curSelectDay=d;
        this._createDayPane(d);
    },
    _decrease:function(type){
        var date=this._convertDate(this.tbarEl.find('.titleCont').attr('value'));
        var y=date.getFullYear();
        var m=date.getMonth();
        var d=date.getDate();
        var d;
        if(type=='Y'){
            d=new Date(y-1,m,d);
        }else if(type=='M'){
            if(m==0){
                d=new Date(y-1,11,d);
            }else{
                d=new Date(y,m-1,d);
            }               
        }
        this.curSelectDay=d;
        this._createDayPane(d);
    },
    _onClick:function(date){
        this.curSelectDay=date;
        if(typeof this.callback[this.currentDOMCalID]=='function'){
            this.callback[this.currentDOMCalID](date);  
            if(this.config.autoHide){
                this.hide();
            }else{
                this._createDayPane(date);
            }
        }
    },
    setPosition:function(_top,_left){
        this.el.css({top:_top, left:_left});
    },
    setConfig:function(config){
        $.extend(this.config,config);
    },
    hide:function(){
        this.el.hide();
    },
    show:function(targetDom,d,direction){
        if(direction!=null&&direction!=''){
            this.config.direction=direction;
        }
        if(d==null||!d){
            this.lastSelectDay=this.curSelectDay=null;
        }else{
            if (typeof d == 'string') {
                if (this._isDate(d)){
                    this.lastSelectDay = this.curSelectDay = this._convertDate(d);
                }else{
                    this.lastSelectDay=this.curSelectDay=null;
                }
            }else{
                this.lastSelectDay = this.curSelectDay = d;
            }
        }
        this._createDayPane(this.lastSelectDay);            
        if(this.config.autoLocation&&targetDom){
            var target=$(targetDom);
            this.currentDOMCalID=target.attr('calID');
            var position = target.offset();
            if (this.config.direction == 'bottom') {
                this.setPosition(position.top + target.outerHeight(), position.left);
            }else if(this.config.direction == 'top'){
                this.setPosition(position.top -this.el.height(), position.left);
            }else if(this.config.direction == 'right'){
                this.setPosition(position.top + target.outerHeight(), position.left-(this.el.width()-target.outerWidth()));
            }
        }
        this.el.show();     
    },
    register:function(dom,callback){
        var key=this.domID++;
        $(dom).attr('calID',key);
        this.callback[key]=callback||function(){};
    },
    unregister:function(dom){
        var id=$(dom).attr('calID');
        this.callback[id]=null;
        delete this.callback[id];
    }
};